#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" NIPAP shell command

    A shell command to interact with NIPAP.
"""
import configparser
import logging
import os
import sys
import socket

import nipap_cli
import nipap_cli.nipap_cli
from nipap_cli.command import Command, CommandError
from pynipap import NipapError
import tracing

# early close of stdout to avoid broken pipe, see #464
# If our output is being piped and the receiver of the pipe is killed off before
# us there won't be a pipe anymore and Python will print an ugly "broken pipe"
# backtrace that could look a bit different depending on which version of Python
# we are running on. To avoid this, we register an atexit handler which will try
# to close stdout before we exit.
import atexit
@atexit.register
def at_exit():
    try:
        sys.stdout.close()
    except:
        pass


class NullHandler(logging.Handler):
    """ A NULL logging handler

        Discards all log messages passed to it.
    """

    def emit(self, record):
        pass



if __name__ == '__main__':
    logger = logging.getLogger()
    nullh = NullHandler()
    logger.addHandler(nullh)

    import argparse
    parser = argparse.ArgumentParser(description='NIPAP command-line client')
    parser.add_argument("-c", "--columns", type=str, help="define columns")
    parser.add_argument("-f", "--force", action="store_true", help="disable interactive prompting of actions")
    parser.add_argument("--show-interpretation", action="store_true", help="show search interpretation")
    parser.add_argument("--version", action="store_true", help="display version information and exit")
    args, cmd_args = parser.parse_known_args()

    # make sure nipaprc exists and has correct permissions
    userrcfile = os.path.expanduser('~/.nipaprc')
    try:
        nipaprc_perms = oct(os.stat(userrcfile).st_mode & 0o777)
    except OSError:
        print("%s file could not be opened, does it exist?" % userrcfile)
        sys.exit(2)

    if nipaprc_perms != '0o600':
        print("Permissions %s for '%s' are too open." % (nipaprc_perms, userrcfile), file=sys.stderr)
        print("HINT: chmod 0600 ~/.nipaprc", file=sys.stderr)
        sys.exit(1)

    try:
        with(open(userrcfile)):
            pass
    except IOError as e:
        print("Failed to read file %s, make sure file is readable by current user" % userrcfile, file=sys.stderr)
        sys.exit(1)

    # config defaults
    cfg_defaults = {
        'hostname': 'localhost',
        'port': 1337,
        'password': None,
        'prefix_list_columns': None,
        'use_ssl': 'false'
    }
    # read configuration
    cfg = configparser.ConfigParser(cfg_defaults, allow_no_value=True)
    cfg.read(['/etc/nipaprc', userrcfile])
    nipap_cli.nipap_cli.cfg = cfg

    if cfg.has_section("tracing"):
        try:
            sampler = None

            if cfg.has_option("tracing", "otel_traces_sampler"):
                trace_sampler = cfg.get("tracing", "otel_traces_sampler")
                from opentelemetry.sdk.trace.sampling import _KNOWN_SAMPLERS
                import dippen
                if trace_sampler not in _KNOWN_SAMPLERS:
                    print(
                        f"Unknown otel_traces_sampler '{trace_sampler}'. Valid samplers are: '{list(_KNOWN_SAMPLERS.keys())}'",
                        file=sys.stderr)
                    sys.exit(1)
                sampler = _KNOWN_SAMPLERS[trace_sampler]

            try:
                use_grpc = True
                try:
                    otlp_endpoint = cfg.get("tracing", "otlp_grpc_endpoint")
                except configparser.NoOptionError:
                    # Send trace via nipapd
                    use_grpc = False
                    use_ssl = cfg.getboolean("global", "use_ssl")
                    otlp_endpoint = ("https://" if use_ssl else "http://") + (cfg.get("global", "hostname") +
                                                                            ":" + cfg.get("global", "port") +
                                                                            "/v1/traces/")
                tracing.init_tracing("nipap-cli", otlp_endpoint, sampler, use_grpc)
            except KeyError:
                pass
        except ImportError:
            print("Opentelemetry dependencies are missing. Tracing is not enabled", file=sys.stderr)

    # setup our configuration
    nipap_cli.nipap_cli.setup_connection()

    if args.version:
        import pynipap
        print("nipap CLI client version:", nipap_cli.__version__)
        print("pynipap version:", pynipap.__version__)

        nipapd_version = 'unknown'
        try:
            pynipap.xmlrpc_uri = "%(protocol)s://%(username)s:%(password)s@%(hostname)s:%(port)s" % {
                    'protocol': nipap_cli.nipap_cli.determine_protocol(),
                    'username': nipap_cli.nipap_cli.cfg.get('global', 'username'),
                    'password': nipap_cli.nipap_cli.cfg.get('global', 'password'),
                    'hostname': nipap_cli.nipap_cli.cfg.get('global', 'hostname'),
                    'port'    : nipap_cli.nipap_cli.cfg.get('global', 'port')
                }
            ao = pynipap.AuthOptions({'authoritative_source': 'nipap'})
            nipapd_version = pynipap.nipapd_version()
        except:
            pass

        print("nipapd version:", nipapd_version)

        sys.exit(0)

    try:
        cmd = Command(nipap_cli.nipap_cli.cmds, cmd_args)
    except (ValueError, CommandError, NipapError) as exc:
        print("Error: %s" % str(exc), file=sys.stderr)
        sys.exit(1)

    # execute command
    if cmd.exe is None:
        print("Incomplete command specified")
        print("valid completions: %s" % " ".join(cmd.next_values()))
        sys.exit(1)

    # for more info on the used exit codes, see:
    #  - http://www.tldp.org/LDP/abs/html/exitcodes.html
    #  - http://people.cs.pitt.edu/~alanjawi/cs449/code/shell/UnixSignals.htm
    try:
        cmd.exe(cmd.arg, cmd.exe_options, args)
    except KeyboardInterrupt:
        # exit code for SIGINT (2) + 128 = 130
        sys.exit(130)
    except socket.error as exc:
        print("Failed to connect to the backend: %s" % str(exc), file=sys.stderr)
        sys.exit(1)
    except (IOError, OSError):
        # exit code for SIGPIPE (13) + 128 = 141
        sys.exit(141)
    except NipapError as exc:
        print("Command failed: %s" % str(exc), file=sys.stderr)
        sys.exit(1)
